package orangeScript.android.helper;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;

import android.app.Activity;
import android.content.Context;
import android.content.res.AssetManager;
import android.util.Log;

import orangeScript.extension.Manager;
import orangeScript.extension.Manager.StreamOpener;
import orangeScript.language.FunctionValue;
import orangeScript.language.ScopeContext;
import orangeScript.language.UndefinedValue;
import orangeScript.language.Value;
import orangeScript.parser.Parser;
import orangeScript.parser.ast.Program;

public class Engine {
	
	public static Engine theEngine = null;

	private final Context applicationContext;
	private final StreamOpener streamOpener;
	private final ScopeContext scriptContext;
	
	
	private class AndroidAssetStreamOpener implements StreamOpener {

		@Override
		public InputStream open(String path) throws IOException {
			AssetManager am = applicationContext.getAssets();
			// android SDK does not take absolute path
			try {
				InputStream is = am.open(path.substring(1));
				Log.e("I/O", String.format("[%s]: opened", path));
				return is;
			} catch (IOException e) {
				Log.e("I/O", String.format("[%s]: unable to open", path));
				throw e;
			}
		}
		
	}
	
	private Engine(Context applicationContext) {
		System.setProperty("ext",  "aa@/ext"); // set extension to android assets /ext directory
		this.applicationContext = applicationContext;
		this.streamOpener = new AndroidAssetStreamOpener();
		Manager.registerStreamOpener("aa", this.streamOpener);
		scriptContext = Program.getScopeContext();
	}
	
	public static Engine get(Activity activity) {
		if (theEngine == null) {
			synchronized(Engine.class) {
				if (theEngine == null) {
					theEngine = new Engine(activity.getApplicationContext());
				}
			}
		}
		
		return theEngine;
	}

	public Value eval(String expr, Value[] args) {
		
		Parser parser = new Parser();
		StringReader sr = new StringReader(expr);
		parser.initialize(sr);
		parser.run();
		Program program = parser.getProgram();
		Value ret = program.execute(scriptContext, args);
		return ret;
	}
	
	public Value eval(String expr) {
		
		Parser parser = new Parser();
		StringReader sr = new StringReader(expr);
		parser.initialize(sr);
		parser.run();
		Program program = parser.getProgram();
		Value ret = program.execute(scriptContext);
		return ret;
	}
	
	public Value callScript(Value value, Value[] args, Value thisObject) {
		if ((value == null) || !(value instanceof FunctionValue)) {
			Log.e("OS", "value is not a function, cannot call");
			return UndefinedValue.instance;
		}
		
		FunctionValue fv = (FunctionValue)value;
		
		return fv.call(args,  thisObject);
	}
	
	public Value runScript(String path, Value[] args)  {
		
		InputStream is = null;
		Reader reader = null;
		try {

			Parser parser = new Parser();
			is = streamOpener.open( path );
			reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
			parser.initialize(reader);
			parser.run();
			Program program = parser.getProgram();
			Value ret = program.execute(scriptContext, args);
			
			return ret;
		} catch (IOException e) {
			return UndefinedValue.instance;
			
		} finally {
			try {
				if (reader != null) {
					reader.close();
				}
				if (is != null) {
					is.close();
				}	
			} catch (IOException e) {
			}
		}
	}
	
}
